package appTool

import (
	"context"
	"gitee.com/kinwyb/appTools/common"
	"gitee.com/kinwyb/appTools/log"
	opentracinglog "github.com/opentracing/opentracing-go/log"
	"github.com/sirupsen/logrus"
	"go.elastic.co/apm/module/apmlogrus"
)

type Context interface {
	Finish()
	Logf(fields ...opentracinglog.Field) Context
	TracingSpan() common.TracingSpan
	Start(operationName string) common.TracingContext
}

// AppContext 上下文
type AppContext struct {
	context.Context                    //默认ctx
	tracing         common.TracingSpan //追踪数据
	TracingType     int                //追踪类型[1=ChildOf,2=FollowsFrom]
	isTracing       bool               //是否追踪
	data            []byte             //附带数据内容
}

// Finish 完成追踪
func (t AppContext) Finish() {
	if t.enableTracing() && t.tracing != nil {
		t.tracing.Finish()
	}
}

// 判断当前上下文是否需要开启追踪
func (t AppContext) enableTracing() bool {
	return common.Tracing && t.isTracing
}

// Logf 日志
func (t AppContext) Logf(fields ...opentracinglog.Field) Context {
	if t.tracing != nil {
		t.tracing.Logf(fields...)
	}
	return t
}

// TracingSpan 追踪信息获取,可能返回nil
func (t AppContext) TracingSpan() common.TracingSpan {
	return t.tracing
}

// Start 启动追踪
func (t AppContext) Start(operationName string) common.TracingContext {
	if t.enableTracing() {
		if t.tracing == nil || t.tracing.Span() == nil { //没有父级span,生成根span
			t.tracing = common.NewTracingSpanStart(operationName)
		} else { //有父级span的按类型延伸子级,如果类型为空的不处理
			switch t.TracingType {
			case common.TracingChild:
				t.tracing = t.tracing.ChildOf(operationName)
			case common.TracingFollowsFrom:
				t.tracing = t.tracing.FollowsFrom(operationName)
			}
		}
	}
	t.TracingType = 0 //清空追踪类型,往后传递没有指定类型时按之前值往下扩展
	return t
}

// NewContext 初始化上下文
func NewContext(operationName string) AppContext {
	ctx := AppContext{
		Context:   context.Background(),
		isTracing: true,
	}
	if operationName != "" && common.Tracing {
		ctx.tracing = common.NewTracingSpanStart(operationName)
	}
	return ctx
}

// NewContextWithTracing 初始化上下文
func NewContextWithTracing(tracingSpan common.TracingSpan) AppContext {
	if !common.Tracing {
		tracingSpan = nil
	}
	return AppContext{
		Context:   context.Background(),
		tracing:   tracingSpan,
		isTracing: true,
	}
}

// Child ChildOf
func (t AppContext) Child() AppContext {
	ret := t
	ret.TracingType = common.TracingChild
	return ret
}

// Follows FollowsFrom
func (t AppContext) Follows() AppContext {
	ret := t
	ret.TracingType = common.TracingFollowsFrom
	return ret
}

func (t AppContext) APMLog(lg *logrus.Entry) *logrus.Entry {
	if lg == nil {
		lg = log.AppTool
	}
	if common.TraceType == common.TraceTypeAPM {
		trace := t.TracingSpan()
		if trace != nil {
			traceContextFields := apmlogrus.TraceContext(trace.Context(context.Background()))
			return lg.WithFields(traceContextFields)
		}
	}
	return lg
}
